<template>
  <!-- 锁定弹窗：禁止点击遮罩关闭（只能通过“离开房间”按钮退出） -->
  <div v-if="visible" class="dialog-overlay" @click.stop>
    <div class="dialog" @click.stop>

      <div class="dialog-header sheet-header">
        <div class="header-left">
          <h3 class="room-title">{{ roomName || '未命名房间' }}</h3>
          <span class="status-chip" :class="roomStatusClass">{{ roomStatusText }}</span>
        </div>
        <div class="header-center">
          <span class="room-id-label">房间ID</span>
          <span class="room-id-value">{{ displayRoomId }}</span>
          <button class="copy-btn" @click="copyRoomId">复制</button>
        </div>
        <div class="header-right">
          <div class="ready-summary">已准备 {{ readyCount }}/{{ members.length }}</div>
          <button
            class="primary-btn enter-game-btn"
            :disabled="!canStartGame || !isCurrentUserHost"
            :title="isCurrentUserHost ? (canStartGame ? '进入游戏界面' : startGameDisabledReason) : '仅房主可进入游戏'"
            @click="(isCurrentUserHost && canStartGame) ? confirmEnterGame() : null"
          >进入游戏</button>
        </div>
      </div>
      <div class="dialog-content">

        
        <!-- 成员列表 -->
        <div class="members-section">
          <div class="section-header">
            <h4>房间成员 ({{ members.length }}/{{ room?.maxPlayers || 5 }})</h4>
            <div class="ready-count">
              已准备: {{ readyCount }}/{{ members.length }}
            </div>
          </div>
          
          <div class="members-grid">
            <div v-for="member in sortedMembers" :key="member.id" class="member-card" :class="{ ready: member.isReady, host: member.isHost, me: member.id === currentUserId }">
              <div class="avatar-circle">
                <img v-if="member.avatar" :src="resolveAvatar(member.avatar)" alt="avatar" />
                <span v-else>{{ getAvatarIcon(member.name || member.id) }}</span>
              </div>
              <div class="member-meta">
                <div class="name-row">
                  <span class="name">{{ member.name || `User_${member.id?.slice(-8)}` }}</span>
                  <span v-if="member.id === currentUserId" class="me-tag">我</span>
                </div>
                <div class="badge-row">
                  <span v-if="member.isHost" class="badge host">房主</span>
                  <span class="badge" :class="member.isReady ? 'ready' : 'not-ready'">{{ member.isReady ? '已准备' : '未准备' }}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="info-panel">
          <div class="info-card">
            <div class="info-row"><span>人数</span><span>{{ members.length }}/{{ roomMaxPlayers }}</span></div>
            <div class="info-row"><span>状态</span><span>{{ roomStatusText }}</span></div>
          </div>
          <div class="actions-bar">
            <button v-if="!currentUserReady" class="primary-btn" @click="toggleReady(true)">准备</button>
            <button v-else class="warn-btn" @click="toggleReady(false)">取消准备</button>
            <button class="ghost-btn" @click="leaveRoom">离开房间</button>
          </div>
        </div>
      </div>
      
      <!-- 错误信息显示 -->
      <div v-if="errorMessage" class="error-message">
        {{ errorMessage }}
      </div>
    </div>
  </div>

  <!-- 进入游戏确认对话框 -->
  <div v-if="showStartConfirm" class="confirm-overlay" @click.stop>
    <div class="confirm-dialog" @click.stop>
      <div class="confirm-header">
        <h3>确认进入游戏</h3>
      </div>
      <div class="confirm-content">
        <p>确定要进入游戏界面吗？</p>
        <p class="confirm-detail">进入后所有玩家将进入游戏界面，可以在界面中开始游戏。</p>
      </div>
      <div class="confirm-actions">
        <button class="cancel-btn" @click="cancelStartGame">取消</button>
        <button class="confirm-btn" @click="handleEnterGame">进入游戏</button>
      </div>
    </div>
  </div>
</template>

<script>
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useSocketStore } from '@/stores/socket'
import { useGameStore } from '@/stores/game'
import { useRoomStore } from '@/stores/room'
import { useUserStore } from '@/stores/user'
import request from '@/utils/request'
import { ElMessage } from 'element-plus'

export default {
  name: 'RoomMembersDialog',
  props: {
    visible: { type: Boolean, default: false },
    room: { type: Object, default: () => null },
    mock: { type: Boolean, default: true }
  },
  emits: ['close', 'game-started'],
  setup(props, { emit }) {
    const router = useRouter()
    const socketStore = useSocketStore()
    const gameStore = useGameStore()
    const roomStore = useRoomStore()
    const userStore = useUserStore()

    const members = ref([])
    const currentUserId = ref('')
    const errorMessage = ref('')
    const showStartConfirm = ref(false)
    const displayRoomId = computed(() => roomStore.currentRoom?.roomId || props.room?.roomId || props.room?.id || '-')
    const roomName = computed(() => roomStore.currentRoom?.roomName || props.room?.roomName || '未命名房间')
    const roomMaxPlayers = computed(() => roomStore.currentRoom?.maxPlayers || props.room?.maxPlayers || 5)
    const roomStatus = computed(() => (roomStore.currentRoom?.status || props.room?.status || 'waiting').toLowerCase())
    const roomStatusText = computed(() => roomStatus.value === 'playing' ? '进行中' : roomStatus.value === 'finished' ? '已结束' : '等待中')
    const roomStatusClass = computed(() => ({ playing: roomStatus.value === 'playing', finished: roomStatus.value === 'finished', waiting: roomStatus.value === 'waiting' }))
    const isCurrentUserHost = computed(() => {
      if (!Array.isArray(members.value)) return false
      // 优先根据房间创建者ID判断
      const creatorId = roomStore.currentRoom?.creatorId
      if (creatorId) return String(creatorId) === String(currentUserId.value)
      // 回退：把第一位视为房主
      return members.value.some(m => m.isHost && String(m.id) === String(currentUserId.value))
    })
    const currentUserReady = computed(() => Array.isArray(members.value) && members.value.some(m => m.id === currentUserId.value && m.isReady))

    // 实时更新成员列表
    const updateMembersFromStore = () => {
      if (roomStore.currentRoom?.members) {
        // sync from store
        members.value = [...roomStore.currentRoom.members]
      } else {
        // fallback to props
        // 如果没有成员数据，尝试从props获取
        if (props.room?.members) {
          const normalizedMembers = props.room.members.map((member, index) => ({
            id: member?.id || member?.userId || `user_${index}`,
            name: member?.name || member?.username || member?.nickname || `User_${member?.id?.slice(-8) || index}`,
            isReady: Boolean(member?.isReady ?? member?.ready ?? false),
            isHost: index === 0 // 第一个成员是房主
          }))
          members.value = normalizedMembers
          // created from props
        } else {
          // 无成员数据则保持空，等待后续 REST/SignalR 同步
          members.value = []
        }
      }
      
      // members ready
    }

    // 同步房间人数到 store.rooms 与 currentRoom
    const syncPlayerCount = () => {
      const roomId = getRoomId()
      if (!roomId) return
      const count = members.value.length
      if (roomStore.currentRoom && roomStore.currentRoom.roomId === roomId) {
        roomStore.currentRoom.playerCount = count
      }
      if (Array.isArray(roomStore.rooms)) {
        const idx = roomStore.rooms.findIndex(r => r.roomId === roomId)
        if (idx >= 0) roomStore.rooms[idx].playerCount = count
      }
    }

    // 统一获取 roomId（优先 store，其次 props）
    const getRoomId = () => {
      return (
        roomStore.currentRoom?.roomId ||
        props.room?.roomId ||
        props.room?.id ||
        null
      )
    }

    // 工具：从本地token中解析uid/sub
    const decodeJwt = (token) => {
      try {
        const payload = token.split('.')[1]
        const json = decodeURIComponent(atob(payload.replace(/-/g, '+').replace(/_/g, '/')).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join(''))
        return JSON.parse(json)
      } catch { return null }
    }
    const getUidFromLocal = () => {
      try {
        const tokenStr = localStorage.getItem('token')
        const tokenObj = tokenStr ? JSON.parse(tokenStr) : null
        const payload = tokenObj?.accessToken ? decodeJwt(tokenObj.accessToken) : null
        return payload?.uid || payload?.user_id || payload?.userId || payload?.sub || null
      } catch { return null }
    }
    // 初始化当前用户ID（更稳健，兼容多字段）
    try {
      currentUserId.value = (
        userStore.user?.id ||
        userStore.user?.Id ||
        userStore.user?.userId ||
        userStore.user?.uid ||
        getUidFromLocal() ||
        currentUserId.value ||
        'current_user'
      )
    } catch {}

    // 监听房间成员变化：以“并集合并”为准，避免错误覆盖导致成员丢失
    watch(() => roomStore.currentRoom?.members, (newMembers) => {
      if (!Array.isArray(newMembers)) return
      if (!Array.isArray(members.value) || members.value.length === 0) {
        members.value = [...newMembers]
        return
      }
      const byId = new Map(members.value.map(m => [String(m.id || m.userId), m]))
      const merged = []
      for (const n of newMembers) {
        const id = String(n?.id || n?.userId)
        const existed = byId.get(id)
        merged.push(existed ? { ...existed, ...n } : { ...n })
        byId.delete(id)
      }
      // 保留本地多出来但服务端暂未带回的（防止短暂回退为1人）
      for (const remain of byId.values()) merged.push(remain)
      members.value = merged
    }, { deep: true })

    // 设置当前用户ID
    const setCurrentUserId = () => {
      currentUserId.value = (
        userStore.user?.id ||
        userStore.user?.Id ||
        userStore.user?.userId ||
        userStore.user?.uid ||
        getUidFromLocal() ||
        ''
      )
    }

    // 初始化
    onMounted(async () => {
      setCurrentUserId()
      updateMembersFromStore()
      
      // 调试信息
      // init
      
      // 设置Socket事件监听器
      const tryJoin = async () => {
        try {
          if (!socketStore.isConnected) {
            await socketStore.connect()
          }
          const roomId = getRoomId()
          if (roomId) {
            await socketStore.joinRoom({ roomId })
            try { await roomStore.fetchRoomInfo(roomId); updateMembersFromStore() } catch (_) {}
          }
        } catch (_) {}
      }
      // 先确保连接并加入组，再注册监听，避免注册被跳过
      await tryJoin()
      setupSocketListeners()
    })

    // 可见时确保已加入房间组
    watch(() => props.visible, async (v) => {
      if (v) {
        try {
          if (!socketStore.isConnected) await socketStore.connect()
          const roomId = getRoomId()
          if (roomId) {
            await socketStore.joinRoom({ roomId })
            try { await roomStore.fetchRoomInfo(roomId); updateMembersFromStore() } catch (_) {}
          }
        } catch (_) {}
      }
    })

    // 保证在发送操作前已连接并加入房间组
    const ensureConnectedAndJoined = async () => {
      try {
        if (!socketStore.isConnected) {
          await socketStore.connect()
        }
        const rid = getRoomId()
        if (rid) {
          await socketStore.joinRoom({ roomId: rid })
        }
      } catch (_) {}
    }

    // 设置Socket事件监听器
    const setupSocketListeners = () => {
      // 监听成员准备状态变化（来自服务端）
      const handleReadyChanged = (data) => {
        try {
          const playerId = data?.playerId || data?.id || data?.userId
          const playerName = data?.username || data?.name
          const isReady = Boolean(data?.isReady ?? data?.ready)

          let updated = false
          if (playerId) {
            if (typeof roomStore.updateMemberReady === 'function') {
              roomStore.updateMemberReady(playerId, isReady)
            }
            updateLocalMemberReady(playerId, isReady)
            updated = true
          } else if (playerName) {
            if (typeof roomStore.updateMemberReady === 'function') {
              roomStore.updateMemberReady(playerName, isReady)
            }
            // 本地按名称兜底
            const m = members.value.find(m => (m.name || '').toLowerCase() === String(playerName).toLowerCase())
            if (m) {
              m.isReady = isReady
              members.value = [...members.value]
              updated = true
            }
          }

          // 如果未能根据 id/name 命中，兜底刷新一次房间信息
          if (!updated) {
            const rid = getRoomId()
            if (rid) {
              try {
                roomStore.fetchRoomInfo(rid).then(() => { updateMembersFromStore() })
              } catch (_) {}
            }
          }
          // 防抖校验：若成员数异常回退（例如从2变1），100ms 后再比对一次，异常则强制刷新
          const rid2 = getRoomId()
          if (rid2) {
            const expectedMin = 1
            setTimeout(() => {
              try {
                if (Array.isArray(members.value) && members.value.length < expectedMin) {
                  roomStore.fetchRoomInfo(rid2).then(() => { updateMembersFromStore() })
                }
              } catch (_) {}
            }, 120)
            // 额外保险：小延迟后再同步一次详情，确保各端 ready 状态完全一致
            setTimeout(() => {
              try { roomStore.fetchRoomInfo(rid2).then(() => { updateMembersFromStore() }) } catch (_) {}
            }, 240)
          }
        } catch (_) {}
      }
      socketStore.on('MemberReadyChanged', handleReadyChanged)
      socketStore.on('PlayerReadyChanged', handleReadyChanged) // 兼容旧事件名

      // 监听成员加入/离开（服务端事件为 PlayerJoined/PlayerLeft）
      socketStore.on('PlayerJoined', (data) => {
        const p = data?.player
        if (!p) return updateMembersFromStore()
        // 增量添加成员（同时更新到全局 store，避免 store 与本地不同步导致后续覆盖）
        try {
          const member = {
            id: p?.id || p?.userId,
            name: p?.username || p?.name,
            avatar: p?.avatar || null,
            color: p?.color || null,
            isReady: Boolean(p?.isReady),
            isHost: String(p?.id || p?.userId) === String(roomStore.currentRoom?.creatorId)
          }
          if (typeof roomStore.addOrUpdateMember === 'function') {
            roomStore.addOrUpdateMember(member)
          }
          const exists = members.value.some(m => (m.id || m.userId) === member.id)
          if (!exists) {
            members.value = [...members.value, member]
          }
          syncPlayerCount()
        } catch (_) {
          updateMembersFromStore()
          syncPlayerCount()
        }
      })
      socketStore.on('PlayerLeft', (data) => {
        const playerId = data?.playerId || data?.id
        if (!playerId) return updateMembersFromStore()
        try {
          if (typeof roomStore.removeMember === 'function') {
            roomStore.removeMember(playerId)
          }
          members.value = members.value.filter(m => (m.id || m.userId) !== playerId)
          syncPlayerCount()
        } catch (_) {
          updateMembersFromStore()
          syncPlayerCount()
        }
      })

      // 监听进入游戏事件
      socketStore.on('EnterGame', () => {
        // 自动跳转到游戏界面
        const roomId = getRoomId()
        if (roomId) {
          router.push(`/game/${roomId}`)
        }
      })

      // 监听进入游戏失败事件
      socketStore.on('EnterGameFailed', (data) => {
        ElMessage.error(data?.message || '进入游戏失败')
      })

      // 监听 GameStarted：收到后立刻关闭对话框并导航
      socketStore.on('GameStarted', (data) => {
        try {
          const rid = data?.roomId || getRoomId()
          if (rid) {
            emit('game-started', rid)
            // 收到开始事件后，强制刷新一次房间状态，避免大厅或弹窗状态仍显示等待中
            try { roomStore.updateRoomStatus(rid, 'playing') } catch (_) {}
          }
        } catch (_) {}
      })

      // 可选：房间状态变化（若后端有）
      socketStore.on('RoomStatusChanged', () => { updateMembersFromStore() })

      // 失败类事件提示
      socketStore.on('GameStartFailed', (data) => {
        const msg = data?.message || '开始游戏失败'
        ElMessage.error(msg)
      })
      socketStore.on('PlayerReadyFailed', (data) => {
        const msg = data?.message || '设置准备状态失败'
        ElMessage.error(msg)
      })
    }

    // 清理Socket监听器
    onUnmounted(() => {
      socketStore.off('MemberReadyChanged')
      socketStore.off('PlayerReadyChanged')
      socketStore.off('MemberJoined')
      socketStore.off('PlayerJoined')
      socketStore.off('MemberLeft')
      socketStore.off('PlayerLeft')
      socketStore.off('RoomStatusChanged')
      socketStore.off('GameStartFailed')
      socketStore.off('PlayerReadyFailed')
      socketStore.off('EnterGame')
      socketStore.off('EnterGameFailed')
    })

    // 本地更新成员准备状态
    const updateLocalMemberReady = (memberId, isReady) => {
      const target = String(memberId || '').toLowerCase()
      const idx = members.value.findIndex(m => String(m.id || m.userId || '').toLowerCase() === target || String(m.name || '').toLowerCase() === target)
      if (idx >= 0) {
        const copied = [...members.value]
        copied[idx] = { ...copied[idx], isReady: !!isReady }
        members.value = copied
      }
    }

    // 切换准备状态
    const toggleReady = async (isReady) => {
      const roomId = getRoomId()
      if (!roomId) { errorMessage.value = '缺少房间ID'; return }
      // 先确保已连接并加入房间组
      await ensureConnectedAndJoined()
      const ok = await socketStore.setPlayerReady(roomId, isReady)
      if (ok) {
        // 成功后再本地更新
        updateLocalMemberReady(currentUserId.value, isReady)
        if (typeof roomStore.updateMemberReady === 'function') {
          roomStore.updateMemberReady(currentUserId.value, isReady)
        }
      } else {
        // 失败则强制刷新一次，保证一致性
        try { await roomStore.fetchRoomInfo(roomId); updateMembersFromStore() } catch (_) {}
        ElMessage.error('设置准备状态失败')
      }
    }

    const readyCount = computed(() => members.value.filter(m => m.isReady).length)
    const canStartGame = computed(() => {
      // 一个人就可以开始游戏
      if (members.value.length < 1) return false
      // 房主必须准备
      const host = members.value.find(m => m.isHost)
      if (!host || !host.isReady) return false
      // 房主准备后就可以开始游戏，不需要等待其他玩家
      return true
    })
    const startGameDisabledReason = computed(() => {
      if (members.value.length < 1) return '房间中没有玩家'
      const host = members.value.find(m => m.isHost)
      if (!host || !host.isReady) return '房主需要先准备'
      return ''
    })

    const getAvatarIcon = (name) => name?.[0]?.toUpperCase() || '😀'
    const resolveAvatar = (fileOrUrl) => {
      if (!fileOrUrl) return ''
      const base = (request?.defaults?.baseURL || '').replace(/\/+$/, '')
      const toApi = (name) => `${base}/users/avatar-file/${encodeURIComponent(name)}`

      // 完整URL
      if (/^https?:\/\//i.test(fileOrUrl)) {
        // 若是直链到 /avatars/ 静态目录，提取文件名走 /users/avatar-file/{name} 以避免静态文件未托管导致404
        try {
          const url = new URL(fileOrUrl)
          const parts = url.pathname.split('/')
          const filename = parts[parts.length - 1]
          if (/\/avatars\//i.test(url.pathname) && filename) return toApi(filename)
        } catch (_) {}
        return fileOrUrl
      }

      // 形如 /avatars/xxxx.png 或 avatars/xxxx.png
      if (/\/avatars\//i.test(fileOrUrl)) {
        const parts = fileOrUrl.split('/')
        const filename = parts[parts.length - 1]
        return filename ? toApi(filename) : ''
      }

      // 其余情况视为文件名（例如 xxx.png 或 GUID_xxx.png）
      return toApi(fileOrUrl)
    }

    const copyRoomId = async () => {
      try {
        const id = displayRoomId.value
        if (!id || id === '-') return
        await navigator.clipboard.writeText(id)
      } catch (e) { }
    }

    // 颜色工具：确保返回标准 #RRGGBB
    const normalizeColor = (c) => {
      if (!c || typeof c !== 'string') return ''
      const v = c.startsWith('#') ? c : `#${c}`
      return /^#?[0-9A-Fa-f]{6}$/.test(c) ? v : ''
    }

    // 房主优先排序
    const sortedMembers = computed(() => {
      const arr = Array.isArray(members.value) ? [...members.value] : []
      return arr.sort((a, b) => {
        if (a?.isHost && !b?.isHost) return -1
        if (!a?.isHost && b?.isHost) return 1
        return 0
      })
    })



    // 确认进入游戏
    const confirmEnterGame = () => {
      showStartConfirm.value = true
    }

    // 取消进入游戏
    const cancelStartGame = () => {
      showStartConfirm.value = false
    }

    // 处理进入游戏
    const handleEnterGame = async () => {
      try {
        const roomId = getRoomId()
        if (!roomId) throw new Error('缺少房间ID')
        
        // 调用SignalR的EnterGame方法
        if (socketStore && socketStore.isConnected) {
          await socketStore.enterGame(roomId)
          // 不再本地强切到 playing，等待服务端 GameCountdown / GameStarted
          showStartConfirm.value = false
          emit('game-started', roomId) // 触发进入游戏事件
        } else {
          throw new Error('Socket未连接')
        }
      } catch (error) { 
        errorMessage.value = error.message || '进入游戏失败'
      }
    }

    // 离开房间
    const leaveRoom = async () => {
      try {
        const roomId = getRoomId()
        if (roomId) {
          await roomStore.leaveRoom(roomId)
          // 退出SignalR组（尽力而为）
          try { socketStore.leaveRoom(roomId) } catch (_) {}
          emit('close')
        }
      } catch (error) { errorMessage.value = '离开房间失败' }
    }

    const handleOverlayClick = () => { emit('close') }

    return {
      members,
      currentUserId,
      errorMessage,
      readyCount,
      canStartGame,
      startGameDisabledReason,
      showStartConfirm,
      getAvatarIcon,
      resolveAvatar,
      normalizeColor,
      toggleReady,
      confirmEnterGame,
      handleEnterGame,
      cancelStartGame,
      handleOverlayClick,
      displayRoomId,
      roomName,
      roomMaxPlayers,
      roomStatusText,
      roomStatusClass,
      isCurrentUserHost,
      currentUserReady,
      copyRoomId,
      leaveRoom
    ,
      sortedMembers
    }
  }
}
</script>

<style scoped>
.dialog-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(15,23,42,.55); display: flex; align-items: center; justify-content: center; z-index: 1000; backdrop-filter: blur(2px); }
.dialog { background: #ffffff; border-radius: 16px; box-shadow: 0 24px 48px rgba(2,6,23,0.18); width: 92%; max-width: 760px; max-height: 90vh; overflow-y: auto; border: 1px solid #eef2f7; }

.sheet-header { padding: 14px 18px; border-bottom: 1px solid #e5e7eb; background: linear-gradient(180deg,#ffffff 0%,#fafafa 100%); display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px; border-top-left-radius: 16px; border-top-right-radius: 16px; }
.header-left { display: flex; align-items: center; gap: 10px; }
.room-title { margin: 0; font-size: 18px; font-weight: 800; color: #0f172a; letter-spacing: .2px; }
.status-chip { padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 600; }
.status-chip.waiting { background: #eef2ff; color: #3730a3; }
.status-chip.playing { background: #ecfdf5; color: #065f46; }
.status-chip.finished { background: #fef2f2; color: #991b1b; }
.header-center { display: flex; align-items: center; gap: 8px; justify-content: center; }
.room-id-label { font-size: 12px; color: #64748b; }
.room-id-value { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; padding: 4px 8px; background: #f3f4f6; border-radius: 8px; font-size: 12px; border: 1px dashed #e5e7eb; }
.header-right { display: flex; align-items: center; gap: 10px; justify-content: flex-end; }
.ready-summary { font-size: 12px; color: #475569; }
.copy-btn { padding: 6px 10px; font-size: 12px; border: 1px solid #d1d5db; background: white; border-radius: 8px; cursor: pointer; box-shadow: 0 1px 0 rgba(0,0,0,.03); }
.copy-btn:hover { background: #f3f4f6; }
.primary-btn { background: #2563eb; color: #fff; border: none; border-radius: 10px; padding: 8px 14px; font-size: 12px; cursor: pointer; box-shadow: 0 6px 12px rgba(37,99,235,.2); transition: transform .15s ease, box-shadow .15s ease; }
.primary-btn:disabled { opacity: .5; cursor: not-allowed; }
.primary-btn:hover { transform: translateY(-1px); box-shadow: 0 8px 16px rgba(37,99,235,.28); }
.warn-btn { background: #f59e0b; color: #fff; border: none; border-radius: 10px; padding: 8px 14px; font-size: 12px; cursor: pointer; box-shadow: 0 6px 12px rgba(245,158,11,.2); }
.ghost-btn { background: #fff; color: #374151; border: 1px solid #e5e7eb; border-radius: 8px; padding: 8px 12px; font-size: 12px; cursor: pointer; }

.dialog-content { padding: 16px; }
.sheet-body { display: grid; grid-template-columns: 1fr 240px; gap: 16px; }

.members-section { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 12px; padding: 12px; }
.section-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.members-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

.member-card {
  display: grid;
  grid-template-columns: 44px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  transition: box-shadow .15s ease, transform .15s ease;
}
.member-card:hover { box-shadow: 0 6px 14px rgba(2,6,23,.06); transform: translateY(-1px); }
.member-card.ready { border-color: #10b981; box-shadow: 0 0 0 2px rgba(16,185,129,0.15) inset; }
.member-card.host { border-color: #f59e0b; }
.member-card.me { outline: 2px dashed #93c5fd; }
.avatar-circle { width: 44px; height: 44px; border-radius: 50%; background: linear-gradient(180deg,#e5e7eb,#f3f4f6); display: flex; align-items: center; justify-content: center; font-weight: 800; color: #111827; }
.avatar-circle img { width: 100%; height: 100%; border-radius: 50%; display: block; }
.member-meta { display: flex; flex-direction: column; gap: 4px; }
.name-row { display: flex; align-items: center; gap: 6px; }
.name { font-weight: 800; color: #0f172a; letter-spacing: .2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.me-tag { font-size: 10px; color: #2563eb; background: #dbeafe; border-radius: 999px; padding: 2px 6px; }
.badge-row { display: flex; align-items: center; gap: 6px; }
.badge { font-size: 10px; padding: 2px 6px; border-radius: 999px; background: #f3f4f6; color: #374151; font-weight: 600; }
.badge.ready { background: #ecfdf5; color: #065f46; }
.badge.not-ready { background: #fef2f2; color: #991b1b; }
.badge.host { background: #fff7ed; color: #9a3412; }

/* 成员颜色点（当前房间成员视图不显示） */
/* .color-dot { width: 10px; height: 10px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px #e5e7eb; display: inline-block; } */

@media (max-width: 920px) {
  .members-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .members-grid { grid-template-columns: 1fr; }
  .member-card { grid-template-columns: 40px 1fr; }
}

.info-panel { display: flex; flex-direction: column; gap: 12px; }
.info-card { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 12px; box-shadow: 0 4px 10px rgba(2,6,23,.04); }
.info-row { display: flex; align-items: center; justify-content: space-between; font-size: 13px; color: #334155; padding: 8px 0; }
.actions-bar { display: flex; gap: 10px; }

.error-message { background: #fef2f2; color: #dc2626; padding: 12px; border-radius: 10px; margin-top: 12px; border: 1px solid #fecaca; }

@media (max-width: 680px) {
  .sheet-body { grid-template-columns: 1fr; }
  .header-center { display: none; }
}

/* 确认对话框样式 */
.confirm-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; z-index: 1100; }
.confirm-dialog { background: white; border-radius: 12px; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); width: 90%; max-width: 400px; }
.confirm-header { padding: 24px 24px 0 24px; border-bottom: 1px solid #e5e7eb; padding-bottom: 16px; }
.confirm-header h3 { margin: 0; color: #111827; font-size: 18px; font-weight: 600; }
.confirm-content { padding: 24px; }
.confirm-content p { margin: 0 0 12px 0; color: #374151; line-height: 1.5; }
.confirm-detail { font-size: 14px; color: #6b7280; margin-top: 8px; }
.confirm-actions { display: flex; gap: 12px; padding: 0 24px 24px 24px; }
.confirm-actions .cancel-btn { flex: 1; padding: 12px 20px; border: 1px solid #d1d5db; background: white; color: #374151; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.2s; }
.confirm-actions .cancel-btn:hover { background: #f9fafb; border-color: #9ca3af; }
.confirm-actions .confirm-btn { flex: 1; padding: 12px 20px; border: none; background: #3b82f6; color: white; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.2s; }
.confirm-actions .confirm-btn:hover { background: #2563eb; }

.action-btn.enter-game-btn { background: #10b981; color: white; border: none; border-radius: 6px; padding: 8px 12px; cursor: pointer; font-weight: 500; }
.action-btn.enter-game-btn:hover { background: #059669; }
.action-btn.enter-game-btn.disabled { background: #9ca3af; cursor: not-allowed; }
.action-btn.enter-game-btn.disabled:hover { background: #9ca3af; }

/* 状态文本样式 */
.host-ready-text { color: #059669; font-weight: 600; }
.host-not-ready-text { color: #d97706; font-weight: 600; }
.ready-text { color: #059669; font-weight: 600; }
.not-ready-text { color: #6b7280; font-weight: 500; }

/* 操作区域样式 */
.host-actions { display: flex; align-items: center; gap: 8px; }
.player-actions { display: flex; align-items: center; gap: 8px; }
</style> 